/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package seguridad;

import java.io.*;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.*;
import java.security.cert.*;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.*;
import javax.crypto.interfaces.PBEKey;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import rsa.mundo.Rsa;
import sun.security.x509.*;

/**
 *
 * @author MI EQUIPO
 */
public class ManejadorCertificados {
    
    private static KeyPair llavesCertificador;
    
     public static KeyPair generarParDeLlavesCertificador() throws Exception {
        
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA", "SUN");
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN" );
        byte[] bytes = new byte[1024/8];
        random.nextBytes(bytes);
        int seedByteCount = 10;
        byte[] seed = random.generateSeed(seedByteCount);
        
         for (int i = 0; i < seed.length; i++) {
             seed[i]=(byte) (i%2);
             
         }
        
        random.setSeed(seed);
        keyGen.initialize(1024,random);
                
        KeyPair keypair = keyGen.generateKeyPair();
        
        return keypair;
    }

     public static PrivateKey cargarLlavePrivada(String rutaArchivo)
     {
            try {
            File archivo = new File( rutaArchivo );
            
                    ObjectInputStream ois = new ObjectInputStream( new FileInputStream( archivo ) );
                    PrivateKey privk = ( PrivateKey )ois.readObject( );
                    
                    ois.close( );
                    
                    return privk;
        } catch (Exception ex) {
            Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
     }
     public static void cargarLlaves(String rutaArchivo)
     {
        try {
            File archivo = new File( rutaArchivo );
            
                    ObjectInputStream ois = new ObjectInputStream( new FileInputStream( archivo ) );
                    PrivateKey privk = ( PrivateKey )ois.readObject( );
                    PublicKey pubk = ( PublicKey )ois.readObject( );
                    
                    llavesCertificador = new KeyPair(pubk, privk);
                    ois.close( );
        } catch (Exception ex) {
            Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
        }
     }
     
     public static void guardarLlavesCertificador(KeyPair kp)
     {
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream( new FileOutputStream("./data/llavesCertificador" ) );
            oos.writeObject( kp.getPrivate() );
            oos.writeObject( kp.getPublic() );
            oos.close( );
        } catch (IOException ex) {
            Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                oos.close();
            } catch (IOException ex) {
                Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
     }
     
      public static void guardarLlavePrivadaPeer(KeyPair kp, String rutaArchivo)
     {
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream( new FileOutputStream(rutaArchivo) );
            oos.writeObject( kp.getPrivate() );
            oos.close( );
        } catch (IOException ex) {
            Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                oos.close();
            } catch (IOException ex) {
                Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
     }
    /**
     * genera un par de llaves publica y simetrica
     *
     * @return
     */
    public static KeyPair generarParDeLlaves() throws Exception {
        
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN" );
        byte[] bytes = new byte[1024/8];
        random.nextBytes(bytes);
        int seedByteCount = 10;
        byte[] seed = random.generateSeed(seedByteCount);
        System.out.println("TamaÃ±o del seed!!!!!!");
        random.setSeed(seed);
        keyGen.initialize(1024,random);
        KeyPair keypair = keyGen.generateKeyPair();
        
        return keypair;
    }
    
    public static void guardarCertificado(X509Certificate certificado, String nombreArchivo) {
        try {
            String file = nombreArchivo;
            
            byte[] buf = certificado.getEncoded();
            
            FileOutputStream os = new FileOutputStream(file);
            
            PrintWriter wr = new PrintWriter(os);
            String codificado = new sun.misc.BASE64Encoder().encode(buf);
            System.out.println(codificado);
            wr.println("-----BEGIN CERTIFICATE-----");
            wr.print(codificado);

//            for (int i = 0; i < codificado.length(); i++) {
//                char caracter = codificado.charAt(i);
//                wr.write(caracter);
//                
//            }
            wr.println("\n-----END CERTIFICATE-----");
            wr.close();
            os.close();
            
            
            
        } catch (Exception ex) {
            Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public static boolean validarCertificado(String rutaArchivo) {
        try {
            
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            List mylist = new ArrayList();
            FileInputStream in = new FileInputStream(rutaArchivo);
            
             X509CertImpl c = (X509CertImpl) cf.generateCertificate(in);
              //X509Certificate c = X509CertImpl.getInstance(certificateData);
            mylist.add(c);
            
            CertPath cp = cf.generateCertPath(mylist);

            // acÃ¡ toma el certificado con las llaves de la certificadora
            //PublicKeyCertificador publicKeyCertificador = new PublicKeyCertificador();
            //PrivateKeyCertificador privateKeyCertificador = new PrivateKeyCertificador();
            //KeyPair kpCertificador = new KeyPair(publicKeyCertificador, privateKeyCertificador);
            
            
            X509Certificate trust = (X509Certificate) generateUnSignedCertificate("cn=dnCompumunoHipermegared", llavesCertificador, 1);
            TrustAnchor anchor = new TrustAnchor((X509Certificate) trust, null);
            PKIXParameters params = new PKIXParameters(Collections.singleton(anchor));
            params.setRevocationEnabled(false);
            CertPathValidator cpv = CertPathValidator.getInstance("DSA");
            
            PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) cpv.validate(cp, params);
            System.out.println("este es el resultado de la validaciÃ³n!!!!!!!");
            System.out.println(result);
           
            
        } catch (Exception ex) {
            //Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
            return true;
        }
        
        return true;
        
    }
    
     public static X509CertImpl leerCertificado(String rutaArchivo) {
        try {
            
             CertificateFactory cf = CertificateFactory.getInstance("X.509");
             FileInputStream in = new FileInputStream(rutaArchivo);
             X509CertImpl c = (X509CertImpl) cf.generateCertificate(in);
             
             return c;
            
            
        } catch (Exception ex) {
            Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return null;
        
    }
    
    public static X509CertImpl generateUnSignedCertificate(String dn, KeyPair pair, int days) throws GeneralSecurityException, IOException {
        PrivateKey privkey = pair.getPrivate();
        X509CertInfo info = new X509CertInfo();
        Date from = new Date();
        Date to = new Date(from.getTime() + days * 86400000l);
        CertificateValidity interval = new CertificateValidity(from, to);
        BigInteger sn = new BigInteger(64, new SecureRandom());
        X500Name owner = new X500Name(dn);
        X500Name issuer = new X500Name("cn=CompumundoHipermegared");
        
        info.set(X509CertInfo.VALIDITY, interval);
        info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
        info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
        info.set(X509CertInfo.ISSUER, new CertificateIssuerName(issuer));
        info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
        info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
        AlgorithmId algo = new AlgorithmId(AlgorithmId.DSA_oid);
        info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));

        // Sign the cert to identify the algorithm that's used.
        X509CertImpl cert = new X509CertImpl(info);

        //cert.sign(privkey, algorithm);

        // Update the algorith, and resign.
        //algo = (AlgorithmId) cert.get(X509CertImpl.SIG_ALG);
        //info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo);
        //cert = new X509CertImpl(info);
        //cert.sign(privkey, algorithm);
        return cert;
    }
    
    public static X509CertImpl signCertificate(X509CertImpl cert, PrivateKey certifierPrivateKey, String signingAlgorithm) throws Exception {
        cert.sign(certifierPrivateKey, signingAlgorithm);
        
        return cert;
    }
    
    public static void main(String[] args) {
        try {
            System.out.println("validando certificado");
            
            
            System.out.println("yo soy la entidad Certificadora");
            
            
            System.out.println("Esta es la llave publica");
       
            System.out.println("FIN llave publica");
            System.out.println("Esta es la llave privada");
            
           
            cargarLlaves("./data/llavesCertificador");
            System.out.println("################################################################");
            System.out.println(llavesCertificador.getPrivate());
            System.out.println("################################################################");
            
     
            System.out.println("FIN llave privada");
            
            System.out.println("Generando par de llaves para un peer");
            KeyPair llavesPeer = generarParDeLlaves();
            
            System.out.println("Generando certificado para el peer");
            X509CertImpl certPeer = generateUnSignedCertificate("cn=dnCompumunoHipermegared", llavesPeer, 30);
            X509CertImpl certFirmado = signCertificate(certPeer, llavesCertificador.getPrivate(), "DSA");
            
            System.out.println("---------------------------------");


 
            
            guardarCertificado(certFirmado, "./data/prueba.cer");
           
            validarCertificado("./data/certificado.cer");
        } catch (Exception ex) {
            Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        
    }
     public static void descifrarArchivo(String rutaArchivo, String contrasenia)
    {
        try 
        {
//            SecretKeySpec skey = new SecretKeySpec(getMD5(contrasenia), "AES");
//                
//                DesEncrypter des = new DesEncrypter( skey );
//                des.cifrarArchivo(rutaArchivo, contrasenia);
            
//            Crypto.cifrarArchivo("./data/textoClaro.txt", contrasenia);
//           
//            Crypto.descifrarArchivo("./data/textoClaro.txt.cifrado", contrasenia);
            System.out.println("descifrando archivo:" +rutaArchivo);
            Crypto.descifrarArchivo(rutaArchivo, contrasenia);
           
          // Crypto.descifrarArchivo("./data/a.jpg", contrasenia);
            
//            Crypto.cifrarArchivo(rutaArchivo, contrasenia);
//            Crypto.descifrarArchivo(rutaArchivo, contrasenia);
        } 
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }
    
    public static void cifrarArchivo(String rutaArchivo, String contrasenia)
    {
        try 
        {
//            SecretKeySpec skey = new SecretKeySpec(getMD5(contrasenia), "AES");
//                
//                DesEncrypter des = new DesEncrypter( skey );
//                des.cifrarArchivo(rutaArchivo, contrasenia);
            
//            Crypto.cifrarArchivo("./data/textoClaro.txt", contrasenia);
//           
//            Crypto.descifrarArchivo("./data/textoClaro.txt.cifrado", contrasenia);
            System.out.println("cifrando archivo:" +rutaArchivo);
            Crypto.cifrarArchivo(rutaArchivo, contrasenia);
           
          // Crypto.descifrarArchivo("./data/a.jpg", contrasenia);
            
//            Crypto.cifrarArchivo(rutaArchivo, contrasenia);
//            Crypto.descifrarArchivo(rutaArchivo, contrasenia);
        } 
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }
    private static byte[] getMD5(String input){
        try{
            byte[] bytesOfMessage = input.getBytes("UTF-8");
            MessageDigest md = MessageDigest.getInstance("MD5");
            return md.digest(bytesOfMessage);
        }  catch (Exception e){
             return null;
        }
    }
    
    public final static String cifrarTextoConLlavePublica(PublicKey llavePublica, String textoClaro)
    {
        try {
            // Iniciar un objeto para la encr / desencr
            Cipher desCipher = Cipher.getInstance( "RSA" );
            // Leer, escribir y encriptar un dato
            String pwd = textoClaro;
            byte[] cleartext = pwd.getBytes( );
            String s1 = new String( cleartext );
            System.out.println( "password original:" + s1 );
            desCipher.init( Cipher.ENCRYPT_MODE, llavePublica );
            byte[] ciphertext = desCipher.doFinal( cleartext );
            String s2 = new String( ciphertext );
            System.out.println( "password encriptado:" + s2 );
            
            return s2;
//            // Ahora desencriptar
//            desCipher.init( Cipher.DECRYPT_MODE, keyPair.getPrivate( ) );
//            byte[] cleartext1 = desCipher.doFinal( ciphertext );
//            String s3 = new String( cleartext1 );
//            System.out.println( "password desencriptado:" + s3 );
            
        } catch (Exception ex) {
            Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
        } 
        return null;
    }
    
    public final static byte[] bytesCifrarTextoConLlavePublica(PublicKey llavePublica, String textoClaro)
    {
        try {
            Rsa cifrador = new Rsa();
            cifrador.asignarLLavePublica(llavePublica);
            // Iniciar un objeto para la encr / desencr
            Cipher desCipher = Cipher.getInstance( "RSA" );
            // Leer, escribir y encriptar un dato
            String pwd = textoClaro;
            byte[] cleartext = pwd.getBytes( );
            
            
            
            String s1 = new String( cleartext );
            System.out.println( "password original:" + s1 );
            desCipher.init( Cipher.ENCRYPT_MODE, llavePublica );
            
            byte[] ciphertext = cifrador.encripta(cleartext, llavePublica);
            
            //byte[] ciphertext = desCipher.doFinal( cleartext );
            
            
            return ciphertext;
//            // Ahora desencriptar
//            desCipher.init( Cipher.DECRYPT_MODE, keyPair.getPrivate( ) );
//            byte[] cleartext1 = desCipher.doFinal( ciphertext );
//            String s3 = new String( cleartext1 );
//            System.out.println( "password desencriptado:" + s3 );
            
        } catch (Exception ex) {
            Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
        } 
        return null;
    }
    
    public final static byte[] bytesDescifrarTextoConLlavePublica(PrivateKey llavePrivada, byte [] bytesCifrados)
    {
        try {
            Rsa cifrador = new Rsa();
            cifrador.asignarLLavePrivada(llavePrivada);
           
            byte[] ciphertext = cifrador.desencriptarPersonalizado(bytesCifrados, llavePrivada);
            
            
            
            return ciphertext;

            
        } catch (Exception ex) {
            Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
        } 
        return null;
    }
    
    public static void generarCertificadoPrimeraVez(String contraseniaLlavePrivada, String nombrePeer) 
    {
        try {
            
            KeyPair llavesPeer = generarParDeLlaves();
            cargarLlaves("./data/llavesCertificador");
            X509CertImpl certPeer = generateUnSignedCertificate("cn="+nombrePeer, llavesPeer, 30);
            X509CertImpl certFirmado = signCertificate(certPeer, llavesCertificador.getPrivate(), "DSA");
            guardarCertificado(certFirmado,"./data/"+nombrePeer+".cer");
            guardarLlavePrivadaPeer(llavesPeer, "./data/"+nombrePeer+".llavePrivada");
            
            //cifrarArchivo("./data/"+nombrePeer+".llavePrivada", contraseniaLlavePrivada);
            
            
            
        } catch (Exception ex) {
            Logger.getLogger(ManejadorCertificados.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public static void firmarObjeto( String rutaObjeto, PrivateKey llavePrivada )
    {
         /* Generate a DSA signature */
    /* Generate a DSA signature */
        try
        {
            /* Create a Signature object and initialize it with the private key */

            Signature rsa = Signature.getInstance( "SHA1withRSA" );

            rsa.initSign( llavePrivada );

            /* Update and sign the data */

            FileInputStream fis = new FileInputStream( rutaObjeto );
            BufferedInputStream bufin = new BufferedInputStream( fis );
            byte[] buffer = new byte[1024];
            int len;
            while( bufin.available( ) != 0 )
            {
                len = bufin.read( buffer );
                rsa.update( buffer, 0, len );
            }
            ;

            bufin.close( );

            /*
             * Now that all the data to be signed has been read in, generate a signature for it
             */

            byte[] realSig = rsa.sign( );

            /* Save the signature in a file */
            FileOutputStream sigfos = new FileOutputStream( "./data/firma" );
            sigfos.write( realSig );

            sigfos.close( );
        }
        catch( Exception e )
        {
            System.err.println( "Caught exception " + e.toString( ) );
        }
    }
    
    public static boolean verificarFirmaObjeto( String rutaArchivoFirma, String rutaArchivoObjeto, PublicKey llavePublica )
    {
        try
        {


            /* input the signature bytes */
            FileInputStream sigfis = new FileInputStream( rutaArchivoFirma );
            byte[] sigToVerify = new byte[sigfis.available( )];
            sigfis.read( sigToVerify );

            sigfis.close( );

            /* create a Signature object and initialize it with the public key */
            Signature sig = Signature.getInstance( "SHA1withRSA" );
            sig.initVerify( llavePublica );

            /* Update and verify the data */

            FileInputStream datafis = new FileInputStream( rutaArchivoObjeto );
            BufferedInputStream bufin = new BufferedInputStream( datafis );

            byte[] buffer = new byte[1024];
            int len;
            while( bufin.available( ) != 0 )
            {
                len = bufin.read( buffer );
                sig.update( buffer, 0, len );
            }
            ;

            bufin.close( );

            boolean verifies = sig.verify( sigToVerify );

            System.out.println( "signature verifies: " + verifies );
            return verifies;
        }
        catch( Exception e )
        {
            System.err.println( "Caught exception " + e.toString( ) );
        }
        return false;
    }
}